package jwt

import (
	"EnjoyWorking/config"
	"EnjoyWorking/model"
	"EnjoyWorking/utils"
	"EnjoyWorking/utils/ijwt"
	"EnjoyWorking/utils/storage"
	"errors"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/kataras/golog"
	"github.com/kataras/iris/v12"
	"strconv"
	"time"
)

var G_JWT *JWT

type (
	JWT struct {
		*ijwt.IJwt
	}
	Claims struct {
		Id     uint  `json:"id"`
		jwt.StandardClaims
	}
)

func InitJWT() {
	iJwt := ijwt.IJwt{}
	G_JWT = &JWT{iJwt.InitJwtConfig()}
}

// Serve the middleware's action
func (j *JWT) ServeHTTP(ctx iris.Context) (err error) {
	var (
		token *jwt.Token
		user  *model.User
	)

	if token, err = j.Check(ctx); err != nil {
		return err
	}

	if token == nil || !token.Valid {
		golog.Errorf("中间件token获取失败")
		return errors.New("中间件token获取失败")
	}

	if user, err = j.Token2Model(token); err != nil {
		return err
	}
	// 检查redis缓存
	if _, err = storage.G_Redis.GetToken(utils.REDIS_ADMIN_FORMAT, strconv.FormatUint(uint64(user.ID), 10)); err != nil {
		return err
	}
	// token校验通过，设置当前用户id到上下文
	ctx.Values().Set(utils.UID, user.ID)
	return nil
}

// Serve the middleware's action
func (j *JWT) ServeWebsocket(ctx iris.Context) {
	var (
		token *jwt.Token
		user  *model.User
		err error
	)
	if token, err = j.Check(ctx); err != nil {
		return
	}

	if user, err = j.Token2Model(token); err != nil {
		return
	}
	if _, err = storage.G_Redis.GetToken(utils.REDIS_ADMIN_FORMAT, strconv.FormatUint(uint64(user.ID), 10)); err != nil {
		return
	}
	// If everything ok then call next.
	ctx.Next()
}

// 在登录成功的时候生成token
func (j *JWT) GenerateToken(user *model.User) (string, error) {
	expireTime := time.Now().Add(time.Duration(config.AppConfig().ExpireTime) * time.Minute)
	tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, Claims{
		user.ID,
		jwt.StandardClaims{
			ExpiresAt: expireTime.Unix(),
			Issuer:    "bst-community-jwt",
		},
	})
	return tokenClaims.SignedString([]byte(config.AppConfig().JwtKey))
}

// 解析token的信息为用户
func (j *JWT) Token2Model(token *jwt.Token) (*model.User, error) {
	//mapClaims := (jwt.Get(ctx).Claims).(jwt.MapClaims)
	var (
		mapClaims, ok = token.Claims.(jwt.MapClaims)
		id            uint
	)
	if !ok {
		return nil, fmt.Errorf("%s", utils.CODE_TOKEN_INVALID.String())
	}

	id = uint(mapClaims["id"].(float64))
	return &model.User{ID:id}, nil
}

func (j *JWT) TokenString2Model(tokenString string) (user *model.User, err error) {
	var token *jwt.Token
	if token, err = jwt.Parse(tokenString, j.Config.ValidationKeyGetter); err != nil {
		return nil, err
	}
	return j.Token2Model(token)
}